﻿
namespace Aeon.Emulator.Instructions.Arithmetic
{
    internal static class Sub
    {
        [Opcode("2C al,ib|80/5 rmb,ib|28/r rmb,rb|2A/r rb,rmb", OperandSize = 16 | 32, AddressSize = 16 | 32)]
        public static void ByteSubtract(Processor p, ref byte dest, byte src)
        {
            int sResult = (sbyte)dest - (sbyte)src;
            uint uResult = (uint)dest - (uint)src;

            dest = (byte)(uResult & 0xFF);

            p.ByteUpdateFlags(sResult, uResult, EFlags.Carry | EFlags.Overflow | EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }

        [Opcode("2D ax,iw|81/5 rmw,iw|83/5 rmw,ibx|29/r rmw,rw|2B/r rw,rmw", AddressSize = 16 | 32)]
        public static void WordSubtract(Processor p, ref ushort dest, ushort src)
        {
            int sResult = (short)dest - (short)src;
            uint uResult = (uint)dest - (uint)src;

            dest = (ushort)(uResult & 0xFFFF);

            p.WordUpdateFlags(sResult, uResult, EFlags.Carry | EFlags.Overflow | EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }
        [Alternate("WordSubtract", AddressSize = 16 | 32)]
        public static void DWordSubtract(Processor p, ref uint dest, uint src)
        {
            long sResult = (long)(int)dest - (long)(int)src;
            ulong uResult = (ulong)dest - (ulong)src;

            dest = (uint)(uResult & 0xFFFFFFFF);

            p.DWordUpdateFlags(sResult, uResult, EFlags.Carry | EFlags.Overflow | EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }
    }

    internal static class Sbb
    {
        [Opcode("1C al,ib|80/3 rmb,ib|18/r rmb,rb|1A/r rb,rmb", OperandSize = 16 | 32, AddressSize = 16 | 32)]
        public static void ByteCarrySub(Processor p, ref byte dest, byte src)
        {
            int sResult = (sbyte)dest - (sbyte)src;
            uint uResult = (uint)dest - (uint)src;

            uint c = (uint)(p.Flags & EFlags.Carry);
            sResult -= (int)c;
            uResult -= c;

            dest = (byte)(uResult & 0xFFu);

            p.ByteUpdateFlags(sResult, uResult, EFlags.Carry | EFlags.Overflow | EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }

        [Opcode("1D ax,iw|81/3 rmw,iw|83/3 rmw,ibx|19/r rmw,rw|1B/r rw,rmw", AddressSize = 16 | 32)]
        public static void WordCarrySub(Processor p, ref ushort dest, ushort src)
        {
            int carry = (int)(p.Flags & EFlags.Carry);
            int sResult = (int)(short)dest - ((int)(short)src + carry);
            uint uResult = (uint)dest - ((uint)src + (uint)carry);

            dest = (ushort)(uResult & 0xFFFFu);

            p.WordUpdateFlags(sResult, uResult, EFlags.Carry | EFlags.Overflow | EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }
        [Alternate("WordCarrySub", AddressSize = 16 | 32)]
        public static void DWordCarrySub(Processor p, ref uint dest, uint src)
        {
            int carry = (int)(p.Flags & EFlags.Carry);
            long sResult = (long)(int)dest - ((long)(int)src + carry);
            ulong uResult = (ulong)dest - ((ulong)src + (ulong)(uint)carry);

            dest = (uint)(uResult & 0xFFFFFFFFu);

            p.DWordUpdateFlags(sResult, uResult, EFlags.Carry | EFlags.Overflow | EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }
    }

    internal static class Dec
    {
        [Opcode("FE/1 rmb", OperandSize = 16 | 32, AddressSize = 16 | 32)]
        public static void ByteDecrement(Processor p, ref byte dest)
        {
            int sResult = (sbyte)dest - (sbyte)1;
            uint uResult = (uint)dest - (uint)1;

            dest = (byte)(uResult & 0xFF);

            p.ByteUpdateFlags(sResult, uResult, EFlags.Overflow | EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }
        
        [Opcode("48+ rw|FF/1 rmw", AddressSize = 16 | 32)]
        public static void WordDecrement(Processor p, ref ushort dest)
        {
            int sResult = (short)dest - (short)1;
            uint uResult = (uint)dest - (uint)1;

            dest = (ushort)uResult;

            p.WordUpdateFlags(sResult, uResult, EFlags.Overflow | EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }
        [Alternate("WordDecrement", AddressSize = 16 | 32)]
        public static void DWordDecrement(Processor p, ref uint dest)
        {
            long sResult = (long)(int)dest - (long)(int)1;
            ulong uResult = (ulong)(uint)dest - (ulong)(uint)1;

            dest = (uint)uResult;

            p.DWordUpdateFlags(sResult, uResult, EFlags.Overflow | EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }
    }

    internal static class Neg
    {
        [Opcode("F6/3 rmb", OperandSize = 16 | 32, AddressSize = 16 | 32)]
        public static void ByteNegate(Processor p, ref sbyte dest)
        {
            sbyte sResult = (sbyte)-dest;
            dest = sResult;

            if(dest != 0)
                p.Flags |= EFlags.Carry;
            else
                p.Flags &= ~EFlags.Carry;

            p.ByteUpdateFlags(sResult, (byte)sResult, EFlags.Overflow | EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }

        [Opcode("F7/3 rmw", AddressSize = 16 | 32)]
        public static void WordNegate(Processor p, ref short dest)
        {
            short sResult = (short)-dest;
            dest = sResult;

            if(dest != 0)
                p.Flags |= EFlags.Carry;
            else
                p.Flags &= ~EFlags.Carry;

            p.WordUpdateFlags(sResult, (ushort)sResult, EFlags.Overflow | EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }
        [Alternate("WordNegate", AddressSize = 16 | 32)]
        public static void DWordNegate(Processor p, ref int dest)
        {
            int sResult = -dest;
            dest = sResult;

            if(dest != 0)
                p.Flags |= EFlags.Carry;
            else
                p.Flags &= ~EFlags.Carry;

            p.DWordUpdateFlags(sResult, (uint)sResult, EFlags.Overflow | EFlags.Sign | EFlags.Zero | EFlags.Parity);
        }
    }
}
